{\rtf1\ansi\ansicpg1252\deff0\deftab480{\fonttbl{\f0\fnil\fcharset0 Verdana;}{\f1\fmodern\fcharset0 Courier;}}
{\colortbl ;\red240\green0\blue31;\red161\green33\blue108;\red0\green0\blue255;\red0\green72\blue255;\red0\green91\blue255;\red242\green0\blue43;\red43\green96\blue67;\red176\green45\blue127;}
{\*\generator Msftedit 5.41.21.2509;}\viewkind4\uc1\pard\lang9\b\f0\fs36 Operation\par
\b0\fs28\par
Typical usage looks like this: \i peg-sharp --out=FooParser.cs Foo.peg\i0 . Foo.peg is a Parsing Expression Grammer which defines the grammar of the language using a syntax which resembles Extended Backus\endash Naur Form (EBNF) augmented with custom code to execute when non-terminals are parsed.\par
\par
FooParser.cs will be created (or over-written) with a back-tracking recursive descent parser using memoization to store intermediate results (i.e. a packrat parser). The memoization allows the parser to operate in linear time. The parser is thread safe as long as each parser instance is used by a single thread (and assuming that the semantic actions are thread safe as well).\par
\par
\b\fs36 Peg Files\par
\b0\fs28\par
Peg files should be utf-8 encoded. Here's an example of a peg file:\par
\par
\cf1\i # Grammar for arithmetic expressions which may contain variables.\cf0\i0\par
\cf1\i # DoCreateBinary is a custom method defined using a partial Parser class..\par
\cf0\i0 start = Start\par
debug = none\par
value = Expression\par
\par
\cf1\i # Start\cf0\i0\par
Start := S (Assignment / Expression);\par
Assignment := Identifier \cf2 '='\cf0  S Expression \cf3\f1 `value = new AssignmentExpression(results[0].Text.Trim(), results[2].Value)`\cf0\f0\par
\par
\cf1\i # Expressions  \cf0\i0\par
Expression := Sum;\par
Identifier := \cf2 [a-zA-Z$]\cf0  \cf2 [a-zA-Z0-9]\cf0 * S \cf3\f1 `value = new VariableExpression(text.Trim())`\cf0\f0\par
Sum := Product ((\cf2 '+'\cf0  / \cf2 '-'\cf0 ) S Product)*  \cf3\f1 `value = DoCreateBinary(results)`\cf0\f0\par
Product := Value ((\cf2 '*'\cf0  / \cf2 '/'\cf0 ) S Value)*  \cf3\f1 `value = DoCreateBinary(results)`\cf0\f0\par
Value := \cf2 [0-9]\cf0 + \cf2 '.'\cf0  \cf2 [0-9]\cf0 + ((\cf2 'e'\cf0  / \cf2 'E'\cf0 ) \cf2 [0-9]\cf0 +)? S \cf3\f1 `value = new FloatExpression(text.Trim())`\cf0\f0\par
Value := \cf2 [0-9]\cf0 + (\cf2 'e'\cf0  / \cf2 'E'\cf0 ) \cf2 [0-9]\cf0 + S \cf3\f1 `value = new FloatExpression(text.Trim())`\cf0\f0\par
Value := \cf2 [0-9]\cf0 + S \cf3\f1 `value = new IntegerExpression(text.Trim())`\cf0\f0\par
Value := Identifier;\par
Value := \cf2 '('\cf0  Expression \cf2 ')'\cf0  S \cf3\f1 `value = results[1].Value`\cf0\f0\par
\par
\cf1\i # Scaffolding\cf0\i0\par
S := Space* \cf3\f1 `text = null`\cf0\f0   \cf1\i # We use a separate space rule because x* always succeeds.\cf0\i0\par
Space := \cf2 [ \\t\\r\\n]\cf0  \cf3\f1 `;`\cf0\f0  \cf3\f1 `expected = "whitespace"`\cf0\f0\par
\par
Peg files consist of two or more settings, optional includes, and one or more non-terminal definitions. Comments may appear at the start or end of lines, start with a '#', and extend to the end of the line.\par
\par
\b Settings\b0  must appear before the non-terminal definitions. The syntax is \i name = value\i0 . These are used to configure the generation of the parser. The supported settings are:\par
\bullet\~\b comment\b0  - Custom class comment. This may appear multiple times for multiple line comments and '// ' will be added to the start if it does not begin with '//'.\par
\bullet\~\b debug\b0  - If this is \i matches\i0  then the text that matches each rule is printed to stdout. If \i failures\i0  then the text that failed and the reason are printed. May also be \i both\i0  or \i none\i0 .\par
\bullet\~\b debug-file\b0  - Name of the file for which to print debug information.\par
\bullet\~\b exclude-exception\b0  - If \i true \i0 then the exception class will not be generated. Defaults to false.\par
\bullet\~\b exclude-methods\b0  - Space separated list of parser method names to not generate. Note that these must still be defined (in a partial class).\par
\bullet\~\b ignore-case\b0  - If true terminals are lower cased before they are compared. Defaults to false.\par
\bullet\~\b namespace\b0  - Name of the namespace to put the parser within. Defaults to the global namespace.\par
\bullet\~\b parse-accessibility \b0 - The accessibility of the parse methods. Defaults to "public".\par
\bullet\~\b start\b0  - The name of the non-terminal with which parsing starts.\par
\bullet\~\b unconsumed\b0  - If this is \i error\i0  (the default if value is not \i void\i0 ) then any input which is not consumed triggers an exception, if it is \i expose\i0  (the default if value is \i void\i0 ) then Parse will not throw and an Unconsumed property is added, and if it is \i ignore\i0  then unconsumed input is completely ignored.\par
\bullet\~\b used\b0  - Space separated list of rule names to consider as used even if they are not referenced.\par
\bullet\~\b using\b0  - Space separated list of custom namespace names to include.\par
\bullet\~\b value\b0  - The name of the type used for the result value of semantic actions. If \i void\i0  then semantic actions have no values and the Parse method returns a count of characters consumed instead of a value.\par
\bullet\~\b visibility\b0  - The visibility of the generated types. Defaults to "internal".\par
\par
Start and value are required settings.\par
\par
\b Includes\b0  can be used to break a peg file into different pieces. The systax is \i include path\i0  where the path is relative to the peg file doing the include. The included file should not have any settings, but may include other files.\par
\b\par
Non-terminals\b0  are used to define the grammar of the language the parser will parse. Each non-terminal is defined by one or more \b rules\b0 . Rules consist of the non-terminal name, a ':=' symbol, a parse expression, and optional semantic actions executed when the rule succeeds or fails. A non-terminal with multiple rules is interpreted as an ordered choice (see below) with the earlier definitions having precedence over the later ones.\par
\par
\b Parse expressions\b0  are used to match input. In general these will attempt to match a portion of the input string and if the match succeeds they will consume that part of the input string. If they fail then no input is consumed and the parser will back-track and try another alternative. If there are no more alternatives the parser will thrown a ParserException.\par
\par
Expressions are written using the following constructs:\par
\b\par
Any\b0  - \i .\i0  will match any character. Note that this will match end of line characters as well. If you don't wish to match these use \i [^\\n\\r]\i0 .\par
\b\par
General Greedy Repetition\b0  - If e is an expression then \i e\{min, max\}\i0  will attempt to match e between min and max times (inclusive). \i e\{0, x\}\i0  will always succeed but may not consume input. \i e\{min,\} \i0 can be used to match between min and an unbounded number. Note that matching will terminate if e matches, but does not consume input.\par
\b\par
Greedy Repetition\b0  - If e is an expression then \i e*\i0  is equivalent to \i e\{0,\}\i0 .\par
\b\par
Greedy Positive Repetition\b0  - If e is an expression then \i e+\i0  is equivalent to \i e\{1,\}\i0 .\par
\b\par
Non-Terminal\b0  - will attempt to match the input using the expressions associated with the named non-terminal.\par
\b\par
Ordered Choice\b0  - If e1 and e2 are expressions then \i e1 / e2\i0  will attempt to match e1 and if that fails e2. If both fail to match then the sequence as a whole fails without consuming any input. Note that unlike the EBNF | operator this is not symmetric.\par
\b\par
Negative Assert\b0  - If e is an expression then \i !e\i0  will fail if e matches and match if e fails. In neither case is input consumed.\par
\b\par
Optional\b0  - If e is an expression then \i e?\i0  is equivalent to \i e\{0, 1\}\i0\par
\b\par
Positive Assert\b0  - If e is an expression then \i &e\i0  will work exactly like \i e\i0  except that input is not consumed if e matches.\par
\b\par
Sequence\b0  - If e1 and e2 are expressions then \i e1 e2\i0  will attempt to match e1 and if that succeeds e2. If either fail to match then the sequence as a whole fails without consuming any input.\par
\b\par
Subexpressions\b0  - Parenthesis can be used to group expressions together.\par
\b\par
Terminal\b0  - \i 'foo' \i0 or \i "foo" \i0 will attempt to match the characters within the quotes against the input. \i [ab0-9] \i0 will attempt to match one character from the input aganst any character in the square brackets. A dash which is not the first or last character is interpreted as an inclusive character range. A ^ as the first character matches any character not in the range.\par
\par
All terminals support hexadecimal escapes (\i\\x20\i0 ) with up to four hex digits. Ranges also support Unicode character category escapes (\i\\cLu\i0 ). <{\field{\*\fldinst{HYPERLINK "http://www.fileformat.info/info/unicode/category/index.htm"}}{\fldrslt{\ul\cf3 http://www.fileformat.info/info/unicode/category/index.htm}}}\f0\fs28 > has a good breakdown of which characters are in the various classes.\par
\par
The precedence is as follows (from highest to lowest):\par
\{\} * + ?\tab repetition, optional\par
! &\tab\tab\tab asserts\par
 \tab\tab\tab sequence\par
/\tab\tab\tab ordered choice\par
\par
\b\fs36 Rule Hooks\par
\b0\fs28\par
Hooks are used to inject code before or after a non-terminal rule parses. This can be useful for context sensitive parsers (e.g. for languages like Python where statement parsing depends upon the indentation of the current line and the previous line). Note that this will usually require updating the m_context field in the parser so memoization works correctly (see the Test18 ftest for an example).\par
\par
The hook syntax consists of a rule name, ':<', ':>', ':>=', or ':>!=', and the code. ':<' is for prolog code, ':>' is for epilog code, ':>=' is for epilog code where the rule matched, and ':>!=' is for epilog code where the rule did not match.\par
\par
Note that both the prolog and the pass epilog may set the fail variable.\par
\par
Here's an example from Test18:\par
\par
\cf1\i # Only try to parse the statement if its indentation matches m_indent.\cf0\i0\par
Statement :<\par
\tab\cf3\f1 `if (!DoIndentMatches(_state.Index))\par
\tab\tab fail = "indentation";`\cf0\f0\par
Statement := IfStatement / PassStatement;\par
\par
\cf1\i # Parse the statements if the first line is more indented than the previous line.\par
# Also sets m_indent to the indent for the current line.\cf0\i0\par
Statements :<\par
\tab\cf3\f1 `int oldIndent = m_indent;\par
\tab if (!DoAdjustIndent(_state.Index))\par
\tab\tab fail = "indentation";`\cf0\f0\par
Statements := Statement+\tab\tab\cf3\f1 `value = new BlockNode(from r in results where r.Value != null select r.Value)`\cf0\f0\par
Statements :>\par
\tab\cf3\f1 `DoRestoreIndent(oldIndent)`\cf0\f0\par
\b\fs36\par
Semantic Actions\par
\b0\fs28\par
Semantic actions appear at the end of rules enclosed in backticks and are used to execute custom code when a non-terminal is parsed. They may contain arbitrary C# code and if the code does not end with a semi-colon or curly brace then a semi-colon will be added. The parser class is a partial class to allow custom helper methods to be defined. The first semantic action is executed if the rule succeeded, the second if it failed.\par
\par
Because the parser may back-track semantic actions should not have side effects. Semantic actions may use the following local variables from the generated code:\par
\par
\b expected\b0  - This is of type System.String. If the semantic action sets this then errors that occur because the parser expected a terminal use this text instead of the terminal text. This may be used only within the failure case.\par
\par
\b fail\b0  - This is of type System.String. If a prolog hook sets this then the rule will fail to match.\par
\par
\b fatal\b0  - This is of type System.String. If a semantic action sets this to a non-null value then a ParserException is thrown using fatal for the message. This may be used only within the success case. You can also directly throw an exception by calling: \b DoThrow\b0 (\cf4\b int\cf0\b0  index, \cf4\b string\cf0\b0  format, \cf4\b params\cf0\b0  \cf4\b object\cf0\b0 [] args).\par
\par
\b results \b0 - This of type List<Result>. Terminals and non-terminals parsed by the rule will add new results to the list. For terminals Value will be set to the default value for the result type. For non-terminals it will be set using the value variable from the rule. The Result type looks something like this:\par
\par
\cf5\b private\cf0\b0  \cf5\b struct\cf0\b0  \b\shad\fs36 Result\b0\shad0\fs28\par
\{\par
\tab\cf6\i // The text which was parsed by the terminal or non-terminal.\cf0\i0\par
\tab\cf5\b public\cf0\b0  \cf5\b string\cf0\b0  \b Text\b0  \{\cf5\b get\cf0\b0 ;\}\par
\tab\par
\tab\cf6\i // The 0-based character index the (non)terminal started on.\cf0\i0\par
\tab\cf5\b public\cf0\b0  \cf5\b int\cf0\b0  \b Index\b0  \{\cf5\b get\cf0\b0 ;\}\par
\tab\par
\tab\cf6\i // The 1-based line number the (non)terminal started on.\cf0\i0\par
\tab\cf5\b public\cf0\b0  \cf5\b int\cf0\b0  \b Line\b0  \{\cf5\b get\cf0\b0 ;\}\par
\tab\par
\tab\cf6\i // The 1-based column number the (non)terminal started on.\cf0\i0\par
\tab\cf5\b public\cf0\b0  \cf5\b int\cf0\b0  \b Col\b0  \{\cf5\b get\cf0\b0 ;\}\par
\tab\par
\tab\cf6\i // For non-terminals this will be the result of the semantic action, \cf0\i0\par
\tab\cf6\i // otherwise it will be the default value. (If the value setting is\par
\tab // void then this property will be omitted).\cf0\i0\par
\tab\cf5\b public\cf0\b0  Expression \b Value\b0  \{\cf5\b get\cf0\b0 ;\}\par
\}\par
\par
\b text\b0  - This is of type System.String and contains all of the text consumed by the rule. If a semantic action sets this to null then the rule will not append a result onto results (setting it to any other value has no effect on the returned Result). This may be used only within the success case.\par
\par
\b value\b0  - The type is determined by the value setting. If the value setting is XmlNode then the value will be set to an XmlElement for non-terminals and an XmlText for terminals, otherwise it will default to results[0].Value. Semantic actions may mutate the node or set the value using the results local variable. The value local may be used only within the success case.\par
\par
For XmlText the Value and Data properties will be the text which was matched. For XmlElement the Name property will be the non-terminal name and line, col, offset, and length attributes may be used. (Note that it's usually easier to use a semantic action like \cf7\b `value.InnerText = text.Trim()` \cf0\b0 instead of offset and length).\cf7\b  \cf0\b0 If a non-terminal has multiple definitions then the corresponding XmlElement will have an alternative attribute with a one based integer value indicating which definition was matched.\par
\par
\b\fs36 Partial Methods\par
\b0\fs28\par
There are a handful of partial methods that you can choose to implement in order to hook into parsing:\par
\par
\cf5\b partial\cf0\b0  \cf5\b void\cf0\b0  \b OnCtorEpilog\b0 ();\par
This is called at the end of the constructor.\par
\cf5\b\par
partial\cf0\b0  \cf5\b void\cf0\b0  \b OnParseProlog\b0 ();\par
This is called immediately before the input is parsed.\par
\cf5\b\par
partial\cf0\b0  \cf5\b void\cf0\b0  \b OnParseEpilog\b0 (State state);\par
This is called after the input is parsed. The state argument has properties to indicate if parsing succeeded, the index of the first unconsumed character, and error messages. \par
\par
\b\fs36 Techniques and Pitfalls\par
\par
\b0\fs28 Unlike most other parsers packrat parsers typically integrate lexing and parsing. This is a nice simplification since there is there is no need for a separate tokenization phase. However it does mean that the grammar needs to explicitly specify \b whitespace\b0  handling. Normally this is done by inserting a whitespace non-terminal immediately after every terminal (see the example above).\par
\par
Because packrat parsers operate on the text instead of tokens they also often must deal with \b reserved words\b0 . This can be done using rules like the below:\par
\par
Identifier := !ReservedWord Letter AlphaNum* Space*;\par
ReservedWord := (\cf8 'if'\cf0  / \cf8 'else'\cf0  / \cf8 'while')\cf0  !AlphaNum;\par
\par
\b Typing\b0  can be a problem because the parser expects that each parse function returns a single type. But it's quite common to want to parse languages which have distinct types for things like statements and expressions. One way to handle this is to define a union-like class. For example,\par
\par
\cf5\b internal\cf0\b0  \cf5\b sealed class\cf0\b0  \b\shad\fs36 Union\b0\shad0\fs28\par
\{\par
\tab\cf5\b public\cf0\b0  \b Union\b0 (Statement s)\par
\tab\{\par
\tab\tab S = s;\par
\tab\}\par
\tab\par
\tab\cf5\b public\cf0\b0  \b Union\b0 (Expression e)\par
\tab\{\par
\tab\tab E = E;\par
\tab\}\par
\tab\par
\tab\cf5\b public\cf0\b0  \b Union\b0 (\cf5\b object\cf0\b0  v)\par
\tab\{\par
\tab\tab V = v;\par
\tab\}\par
\tab\par
\tab\cf5\b public\cf0\b0  Statement \b S\b0  \{\cf5\b get\cf0\b0 ; \cf5\b private\cf0\b0  \cf5\b set\cf0\b0 ;\}\par
\tab\par
\tab\cf5\b public\cf0\b0  Expression \b E\b0  \{\cf5\b get\cf0\b0 ; \cf5\b private\cf0\b0  \cf5\b set\cf0\b0 ;\}\par
\tab\par
\tab\cf5\b public\cf0\b0  \cf5\b object\cf0\b0  \b V\b0  \{\cf5\b get\cf0\b0 ; \cf5\b private\cf0\b0  \cf5\b set\cf0\b0 ;\}\par
\}\par
\par
Note that you'll probably also want to use the exclude-methods setting so that you can replace Parse with one that returns a non-Union type.\par
\par
Another approach is to incrementally construct an object as non-terminals are parsed. For example, you can define a class with automatic properties corresponding to selected non-terminals and use object initializors to construct them. \par
\par
By default peg-sharp will find only a single parse error. So, if you want to find multiple parse errors or try to extract as much information as you can from input which may not be valid you'll have to implement some form of \b error recovery\b0 . This can be done by augmenting selected non-terminals with an expression which will eat characters until something parseable is found. For example, something like the below can be used to recover from parser errors within method bodies:\par
\par
CompilationUnit := Class*;\par
\par
Class := ClassSignature \cf8 '\{'\cf0  Method* \cf8 '\}'\cf0 ;\par
\par
ClassSignature := Visibility? \cf8 'class'\cf0  Identifier Bases?;\par
\par
Method := MethodSignature MethodBody;\par
\par
MethodSignature := Access? Type Identifier Args;\par
\par
\cf6\i # If we can't parse a method body then we'll eat tokens until we\cf0\i0\par
\cf6\i # find something that looks like a method or class.\cf0\i0\par
MethodBody := \cf8 '\{'\cf0  Statement+ \cf8 '\}'\cf0  / RecoverMethod+;\par
\par
RecoverMethod := !MethodOrClassSig .;\par
\par
MethodOrClassSig := MethodSignature / \cf8 '\}'\cf0  ClassSignature;\par
\par
Access := \cf8 'public'\cf0  / \cf8 'private'\cf0  / \cf8 'protected'\cf0  / \cf8 'internal'\cf0 ;\par
\par
Visibility := \cf8 'public'\cf0  / \cf8 'internal'\cf0 ;\par
\par
Note that this technique needs to be used carefully however because it interferes with the normal backtracking behavior of the parser. The above, for example, is OK only if the only input which can match MethodSignature is a method.\par
\par
Many parsers support \b left recursion\b0  and grammars often make use of it. However peg-sharp will error our if it is used because it leads to infinite recursion. It's usually easy to rewrite left recursive rules however. For example, instead of:\par
\par
Additive := Additive \cf8 '+'\cf0  Multitive;\tab\cf6\i # illegal left recursion\cf0\i0\par
Additive := Additive \cf8 '-'\cf0  Multitive;\par
Additive := Multitive;\par
\par
You can write:\par
\par
Additive := Multitive ((\cf8 '+'\cf0  / \cf8 ' -'\cf0 ) Multitive)*;\par
\par
\b\fs36 Peg File Grammar\par
\b0\fs28\par
Action := \cf2 '`'\cf0  \cf2 [^`]\cf0 + \cf2 '`'\cf0\par
\par
Any := \cf2 '.'\cf0  S\par
\par
AssertExpression := \cf2 '&'\cf0  PostfixExpression\par
AssertExpression := \cf2 '!'\cf0  PostfixExpression\par
AssertExpression := PostfixExpression\par
\par
Comment := \cf2 '#'\cf0  \cf2 [^\\n\\r]\cf0 * S\par
\par
Expression := SequenceExpression (\cf2 '/'\cf0  S SequenceExpression)*\par
\par
Identifier := \cf2 [a-zA-Z_]\cf0 + (\cf2 [+-]\cf0  \cf2 [a-zA-Z_0-9]\cf0 +)+ S\par
Identifier := \cf2 [a-zA-Z_]\cf0  \cf2 [a-zA-Z_0-9]\cf0 * S\par
\par
Include := \cf2 'include'\cf0  S Path\par
\par
IncludedFile := S Comment* (Include / Rule / Comment)+\par
\par
Integer := \cf2 [0-9]\cf0 + S \par
\par
Literal := \cf2 '\\''\cf0  (\cf2 '{\field{\*\fldinst{HYPERLINK "\\\\\\\\\\\\'"}}{\fldrslt{\ul\cf3\\\\\\'}}}\f0\fs28 '\cf0  / \cf2 '\\x5c\\x5c'\cf0  / \cf2 [^']\cf0 )+ \cf2 '\\''\cf0  S \par
Literal := \cf2 '"'\cf0  (\cf2 '{\field{\*\fldinst{HYPERLINK "\\\\\\\\""}}{\fldrslt{\ul\cf3\\\\"}}}\f0\fs28 '\cf0  / \cf2 '\\x5c\\x5c'\cf0  / \cf2 [^"]\cf0 )+ \cf2 '"'\cf0  S\par
\par
Path := PathComponent (\cf2 '/'\cf0  PathComponent)* (\cf2 '.'\cf0  PathName)* S\par
\par
PathComponent := \cf2 '..'\cf0\par
PathComponent := \cf2 '.'\cf0\par
PathComponent := PathName\par
\par
PathName := \cf2 [a-zA-Z0-9_-]\cf0 +\par
\par
PegFile := S (Setting / Comment)+ (Include / Rule / RuleEpilogFail / RuleEpilogPass / RuleEpilog / RuleProlog / Comment)+\par
\par
PostfixExpression := PrimitiveExpression \cf2 '\{'\cf0  S Integer \cf2 ','\cf0  S Integer \cf2 '\}'\cf0  S\par
PostfixExpression := PrimitiveExpression \cf2 '\{'\cf0  S Integer \cf2 ','\cf0  S \cf2 '\}'\cf0  S \par
PostfixExpression := PrimitiveExpression \cf2 '*'\cf0  S\par
PostfixExpression := PrimitiveExpression \cf2 '+'\cf0  S\par
PostfixExpression := PrimitiveExpression \cf2 '?'\cf0  S\par
PostfixExpression := PrimitiveExpression\par
\par
PrimitiveExpression := Any / Literal / Range / SubRule / SubExpression\par
\par
Range := \cf2 '['\cf0  (\cf2 '{\field{\*\fldinst{HYPERLINK "\\\\\\\\]"}}{\fldrslt{\ul\cf3\\\\]}}}\f0\fs28 '\cf0  / \cf2 '{\field{\*\fldinst{HYPERLINK "\\\\\\\\\\\\\\\\"}}{\fldrslt{\ul\cf3\\\\\\\\}}}\f0\fs28 '\cf0  / \cf2 [^\\]]\cf0 )+ \cf2 ']'\cf0  S\par
\par
Rule := Identifier \cf2 ':='\cf0  S Expression (\cf2 ';'\cf0  / Action S Action?) S\par
\par
RuleEpilog := Identifier \cf2 ':>'\cf0  S Action S\par
RuleEpilogFail := Identifier \cf2 ':>!='\cf0  S Action S\par
RuleEpilogPass := Identifier \cf2 ':>='\cf0  S Action S\par
RuleProlog := Identifier \cf2 ':<'\cf0  S Action S\par
\par
S := Space*\par
\par
SequenceExpression := AssertExpression+\par
\par
Setting := Identifier \cf2 '='\cf0  S \cf2 'none'\cf0  S\par
Setting := Identifier \cf2 '='\cf0  S Value S\par
\par
Space := \cf2 [ \\t\\r\\n]\cf0\par
\par
SubExpression := \cf2 '('\cf0  S Expression \cf2 ')'\cf0  S\par
\par
SubRule := Identifier\par
\par
Value := \cf2 [^\\t\\n\\r]\cf0 +\par
\par
}
 